[PATCH 03/24] lib-mail: Limit the number of RFC2231 parameters that can be parsed
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 24 Feb 2026 11:11:14 +0000 (13:11 +0200)
committerNoah Meyerhans <noahm@debian.org>
Tue, 31 Mar 2026 19:07:17 +0000 (15:07 -0400)
This avoids excessive CPU usage especially in result_append().

Gbp-Pq: Name CVE-2026-27859.patch

src/lib-mail/rfc2231-parser.c
src/lib-mail/rfc822-parser.h
src/lib-mail/test-rfc2231-parser.c

index 2635a9a80c7ddbcf14f427b194ebab524ad82206..80e07677b9cfd421ec390f14845eef675f2c1dad 100644 (file)
@@ -203,7 +203,7 @@ int rfc2231_parse(struct rfc822_parser_context *ctx,
        struct rfc2231_parameter rfc2231_param;
        const char *key, *p, *p2;
        string_t *str;
-       unsigned int i, j, count, next, next_idx;
+       unsigned int i, j, count, next, next_idx, params_count = 0;
        bool ok, broken = FALSE;
        const char *prev_replacement_str;
        int ret;
@@ -221,6 +221,8 @@ int rfc2231_parse(struct rfc822_parser_context *ctx,
        t_array_init(&rfc2231_params_arr, 8);
        str = t_str_new(64);
        while ((ret = rfc822_parse_content_param(ctx, &key, str)) != 0) {
+               if (++params_count > RFC2231_MAX_PARAMS)
+                       break;
                if (ret < 0) {
                        /* try to continue anyway.. */
                        broken = TRUE;
index 84e9bda67137cc14a2ee4149b9c058455b38a3d8..8d623871f764dc30e51ef6b4cf8e82507b436b79 100644 (file)
@@ -3,6 +3,11 @@
 
 #include "unichar.h"
 
+/* Maximum number of parameters to parse. After this the rest of the parameters
+   are skipped. This is to avoid excessive CPU usage that can be caused by
+   merging of these parameters. */
+#define RFC2231_MAX_PARAMS 128
+
 /* This can be used as a common NUL replacement character */
 #define RFC822_NUL_REPLACEMENT_STR UNICODE_REPLACEMENT_CHAR_UTF8
 
index 76b62a03df618492beaf0b771adc806eabd2897b..3536bb14550ea2456c08fb542942304bacc1aabb 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "str.h"
 #include "rfc822-parser.h"
 #include "rfc2231-parser.h"
 #include "test-common.h"
@@ -249,6 +250,34 @@ static void test_rfc2231_parser_multi_encodings(void)
        run_test("rfc2231 parser invalid encoding", input, sizeof(input)-1, output);
 }
 
+static void test_rfc2231_parser_limits(void)
+{
+       string_t *input = t_str_new(1024);
+
+       test_begin("rfc2231 parser limits");
+       str_append(input, "; ");
+       for (unsigned int i = 0; i < 1100; i++)
+               str_printfa(input, "a%u=b%u; ", i, i);
+       struct rfc822_parser_context parser;
+       const char *const *result;
+       rfc822_parser_init(&parser, str_data(input), str_len(input), NULL);
+       test_assert(rfc2231_parse(&parser, &result) == 0);
+
+       unsigned int count = str_array_length(result);
+       test_assert(count == RFC2231_MAX_PARAMS * 2);
+       for (unsigned int i = 0; i < count; i += 2) {
+               str_truncate(input, 0);
+               str_printfa(input, "a%u", i / 2);
+               test_assert_strcmp_idx(result[i], str_c(input), i);
+
+               str_truncate(input, 0);
+               str_printfa(input, "b%u", i / 2);
+               test_assert_strcmp_idx(result[i + 1], str_c(input), i);
+       }
+       rfc822_parser_deinit(&parser);
+       test_end();
+}
+
 int main(void)
 {
        static void (*const test_functions[])(void) = {
@@ -264,6 +293,7 @@ int main(void)
                test_rfc2231_parser_encodings,
                test_rfc2231_parser_utf8_encoding,
                test_rfc2231_parser_multi_encodings,
+               test_rfc2231_parser_limits,
                NULL
        };
        return test_run(test_functions);